#define PLUGIN_VERSION 		"1.0"
#pragma semicolon 			1
#include <sourcemod>
#include <sdktools>
#include "entities.inc"

#define CVAR_FLAGS			FCVAR_PLUGIN|FCVAR_NOTIFY
#define CHAT_TAG			"\x04[\x05商店\x04] \x01"
#define CONFIG_SPAWNS		"data/l4d2_shop_area_ex.cfg"
#define MAX_SPAWNS			99		//建立物品上限
#define	MAX_SLOTS			16

#define	MODEL_BUY_TYPE_1	   "models/props_vehicles/bus01.mdl"	//客巴
#define	MODEL_BUY_TYPE_2	   "models/props_vehicles/police_car_city.mdl"	//市内警车
#define	MODEL_BUY_TYPE_3	   "models/props_vehicles/taxi_city.mdl"	//市内计程车
#define	MODEL_BUY_TYPE_4	   "models/props/cs_assault/forklift.mdl"   //叉车
#define	MODEL_BUY_TYPE_5	   "models/props_vehicles/train_box.mdl"   //火车厢
#define	MODEL_BUY_TYPE_6	   "models/props_vehicles/helicopter_news_downed.mdl"   //采访直升机(残骸)
#define	MODEL_BUY_TYPE_7	   "models/props_interiors/sofa.mdl"   //沙发(高档真皮三座)
#define	MODEL_BUY_TYPE_8	   "models/props/cs_militia/bar01.mdl"   //酒吧台
#define	MODEL_BUY_TYPE_9	   "models/props/cs_office/shelves_metal.mdl"   //陈列架子(铁制)
#define	MODEL_BUY_TYPE_10	   "models/props_unique/haybails_single.mdl"   //草捆
#define	MODEL_BUY_TYPE_11	   "models/props_street/trashbin01.mdl"   //街道垃圾桶II(塑料制绿色)
#define	MODEL_BUY_TYPE_12	   "models/props_debris/dead_cow_smallpile.mdl"   //牛的尸体(一堆)
#define	MODEL_BUY_TYPE_13	   "models/props/cs_assault/box_stack1.mdl"   //盒子堆(高版）
#define	MODEL_BUY_TYPE_14	   "models/props_equipment/cargo_container02.mdl"   //船货集装箱(白色打开的)
#define	MODEL_BUY_TYPE_15	   "models/props_mall/mall_kiosk.mdl"   //商场摆摊亭子A(关闭的）
#define	MODEL_BUY_TYPE_16	   "models/props/cs_office/vending_machine.mdl"   //自动贩卖机
#define	MODEL_BUY_TYPE_17	   "models/props_wasteland/powertower01.mdl"   //高压电塔
#define	MODEL_BUY_TYPE_18	   "models/props_wasteland/light_spotlight01_lamp.mdl"   //大射灯(铁制落地）
#define	MODEL_BUY_TYPE_19	   "models/props_foliage/swamp_cluster_water.mdl"   // 柏树群(带石头巨型)
#define	MODEL_BUY_TYPE_20	   "models/props_interiors/bed.mdl"   //床(家居式)
#define	MODEL_BUY_TYPE_21	   "models/props_foliage/mall_grass_bush01.mdl"   //草丛III(翠绿色)
#define	MODEL_BUY_TYPE_22	   "models/props_plants/bush.mdl"   //草丛IV(黄绿色带小花)

static	Handle:g_hCvarMPGameMode, Handle:g_hCvarModes, Handle:g_hCvarModesOff, Handle:g_hCvarModesTog, Handle:g_hCvarAllow,
		Handle:g_hCvarGlowCol, g_iCvarGlow, g_iCvarGlowCol,
		bool:g_bCvarAllow, bool:g_bLeft4Dead2, bool:g_bLoaded, g_iPlayerSpawn, g_iRoundStart, g_iSpawnCount,
		g_iSpawns[MAX_SPAWNS][MAX_SLOTS + 2],
		Handle:g_hMenuList, Handle:g_hMenuAng, Handle:g_hMenuPos;
		
new g_iLastCreateEntity = -1;	// 上次创建的物体

// ====================================================================================================
//					PLUGIN INFO / START / END
// ====================================================================================================
public Plugin:myinfo =
{
	name = "商店扩展-限定区域购买",
	author = "Yd社区",
	description = "创建商店购物站并给予购物范围",
	version = PLUGIN_VERSION,
	url = "http://www.l4d.cn"
}

public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
	decl String:sGameName[12];
	GetGameFolderName(sGameName, sizeof(sGameName));
	if( strcmp(sGameName, "left4dead", false) == 0 ) g_bLeft4Dead2 = false;
	else if( strcmp(sGameName, "left4dead2", false) == 0 ) g_bLeft4Dead2 = true;
	else
	{
		strcopy(error, err_max, "Plugin only supports Left 4 Dead 1 & 2.");
		return APLRes_SilentFailure;
	}
	return APLRes_Success;
}

public OnPluginStart()
{
	g_hCvarAllow =		CreateConVar(	"l4d2_shop_area_ex_allow",			"1",			"0=插件关, 1=插件开", CVAR_FLAGS );
	g_hCvarGlowCol =	CreateConVar(	"l4d2_shop_area_ex_glow_color",		"255 255 0",	"0=默认发光颜色. Three values between 0-255 separated by spaces. RGB: Red Green Blue.", CVAR_FLAGS );
	g_hCvarModes =		CreateConVar(	"l4d2_shop_area_ex_modes",			"",				"Turn on the plugin in these game modes, separate by commas (no spaces). (Empty = all).", CVAR_FLAGS );
	g_hCvarModesOff =	CreateConVar(	"l4d2_shop_area_ex_modes_off",		"",				"Turn off the plugin in these game modes, separate by commas (no spaces). (Empty = none).", CVAR_FLAGS );
	g_hCvarModesTog =	CreateConVar(	"l4d2_shop_area_ex_modes_tog",		"0",			"Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together.", CVAR_FLAGS );
	CreateConVar(						"l4d2_shop_area_ex_version",			PLUGIN_VERSION, "Gun Cabinet plugin version.", CVAR_FLAGS|FCVAR_REPLICATED|FCVAR_DONTRECORD);
	//AutoExecConfig(true,				"l4d2_shop_area_ex");

	g_hCvarMPGameMode = FindConVar("mp_gamemode");
	HookConVarChange(g_hCvarMPGameMode,		ConVarChanged_Allow);
	HookConVarChange(g_hCvarAllow,			ConVarChanged_Allow);
	HookConVarChange(g_hCvarModes,			ConVarChanged_Allow);
	HookConVarChange(g_hCvarModesOff,		ConVarChanged_Allow);
	HookConVarChange(g_hCvarModesTog,		ConVarChanged_Allow);
	HookConVarChange(g_hCvarGlowCol,		ConVarChanged_Cvars);
	

	RegAdminCmd("sm_shop_cabinet",			CmdCabinetMenu,		ADMFLAG_ROOT, 	"呼出菜单");
	//RegAdminCmd("sm_shop_cabinet_del",		CmdCabinetDel,		ADMFLAG_ROOT, 	"删除准心的（对准商店上部才能删到）");
	//RegAdminCmd("sm_shop_cabinet_del_last",	CmdCabinetDelLast,	ADMFLAG_ROOT, 	"删除上次创建的物件（只可递归一次使用）");
	//RegAdminCmd("sm_shop_cabinet_clear",	CmdCabinetClear,	ADMFLAG_ROOT, 	"临时删除本小张图所有商店（无用功能）");
	//RegAdminCmd("sm_shop_cabinet_wipe",		CmdCabinetWipe,		ADMFLAG_ROOT, 	"删除本小张图所有商店");
	RegAdminCmd("sm_shop_cabinet_glow",		CmdCabinetGlow,		ADMFLAG_ROOT, 	"商店临时发光");
	RegAdminCmd("sm_shop_cabinet_list",		CmdCabinetList,		ADMFLAG_ROOT, 	"显示清单");
	RegAdminCmd("sm_shop_cabinet_reload",	CmdCabinetReload,	ADMFLAG_ROOT, 	"重置本小张图所有商店");
	RegAdminCmd("sm_shop_cabinet_tele",		CmdCabinetTele,		ADMFLAG_ROOT, 	"瞬移到几号商店 如：!shop_cabinet_tele 1");
	RegAdminCmd("sm_shop_cabinet_ang",		CmdCabinetAng,		ADMFLAG_ROOT, 	"显示菜单来调整准星前的角度");
	RegAdminCmd("sm_shop_cabinet_pos",		CmdCabinetPos,		ADMFLAG_ROOT, 	"显示菜单来调整准星前的位置");	
}

public OnPluginEnd()
{
	ResetPlugin();
}

public OnMapStart()
{
	// 初始化模型
	PrecacheModel(MODEL_BUY_TYPE_1);
	PrecacheModel(MODEL_BUY_TYPE_2);
	PrecacheModel(MODEL_BUY_TYPE_3);
	PrecacheModel(MODEL_BUY_TYPE_4);
	PrecacheModel(MODEL_BUY_TYPE_5);
	PrecacheModel(MODEL_BUY_TYPE_6);
	PrecacheModel(MODEL_BUY_TYPE_7);
	PrecacheModel(MODEL_BUY_TYPE_8);
	PrecacheModel(MODEL_BUY_TYPE_9);
	PrecacheModel(MODEL_BUY_TYPE_10);
	PrecacheModel(MODEL_BUY_TYPE_11);
	PrecacheModel(MODEL_BUY_TYPE_12);
	PrecacheModel(MODEL_BUY_TYPE_13);
	PrecacheModel(MODEL_BUY_TYPE_14);
	PrecacheModel(MODEL_BUY_TYPE_15);
	PrecacheModel(MODEL_BUY_TYPE_16);
	PrecacheModel(MODEL_BUY_TYPE_17);
	PrecacheModel(MODEL_BUY_TYPE_18);
	PrecacheModel(MODEL_BUY_TYPE_19);
	PrecacheModel(MODEL_BUY_TYPE_20);
	PrecacheModel(MODEL_BUY_TYPE_21);
	PrecacheModel(MODEL_BUY_TYPE_22);
}

public OnMapEnd()
{
	ResetPlugin(false);
}
// 获取物体颜色
GetColor(Handle:cvar)
{
	decl String:sTemp[12], String:sColors[3][4];
	GetConVarString(cvar, sTemp, sizeof(sTemp));
	ExplodeString(sTemp, " ", sColors, 3, 4);

	new color;
	color = StringToInt(sColors[0]);
	color += 256 * StringToInt(sColors[1]);
	color += 65536 * StringToInt(sColors[2]);
	return color;
}

// ====================================================================================================
//					CVARS
// ====================================================================================================
public OnConfigsExecuted()
	IsAllowed();

public ConVarChanged_Cvars(Handle:convar, const String:oldValue[], const String:newValue[])
	GetCvars();

public ConVarChanged_Allow(Handle:convar, const String:oldValue[], const String:newValue[])
	IsAllowed();

GetCvars()
{
	g_iCvarGlowCol		= GetColor(g_hCvarGlowCol);
}

IsAllowed()
{
	new bool:bCvarAllow = GetConVarBool(g_hCvarAllow);
	new bool:bAllowMode = IsAllowedGameMode();
	GetCvars();

	if( g_bCvarAllow == false && bCvarAllow == true && bAllowMode == true )
	{
		LoadSpawns();
		g_bCvarAllow = true;
		HookEvent("player_spawn",		Event_PlayerSpawn,	EventHookMode_PostNoCopy);
		HookEvent("round_start",		Event_RoundStart,	EventHookMode_PostNoCopy);
		HookEvent("round_end",			Event_RoundEnd,		EventHookMode_PostNoCopy);
	}

	else if( g_bCvarAllow == true && (bCvarAllow == false || bAllowMode == false) )
	{
		ResetPlugin();
		g_bCvarAllow = false;
		UnhookEvent("player_spawn",		Event_PlayerSpawn,	EventHookMode_PostNoCopy);
		UnhookEvent("round_start",		Event_RoundStart,	EventHookMode_PostNoCopy);
		UnhookEvent("round_end",		Event_RoundEnd,		EventHookMode_PostNoCopy);
	}
}

static g_iCurrentMode;

bool:IsAllowedGameMode()
{
	if( g_hCvarMPGameMode == INVALID_HANDLE )
		return false;

	new iCvarModesTog = GetConVarInt(g_hCvarModesTog);
	if( iCvarModesTog != 0 )
	{
		g_iCurrentMode = 0;

		new entity = CreateEntityByName("info_gamemode");
		DispatchSpawn(entity);
		HookSingleEntityOutput(entity, "OnCoop", OnGamemode, true);
		HookSingleEntityOutput(entity, "OnSurvival", OnGamemode, true);
		HookSingleEntityOutput(entity, "OnVersus", OnGamemode, true);
		HookSingleEntityOutput(entity, "OnScavenge", OnGamemode, true);
		AcceptEntityInput(entity, "PostSpawnActivate");
		AcceptEntityInput(entity, "Kill");

		if( g_iCurrentMode == 0 )
			return false;

		if( !(iCvarModesTog & g_iCurrentMode) )
			return false;
	}

	decl String:sGameModes[64], String:sGameMode[64];
	GetConVarString(g_hCvarMPGameMode, sGameMode, sizeof(sGameMode));
	Format(sGameMode, sizeof(sGameMode), ",%s,", sGameMode);

	GetConVarString(g_hCvarModes, sGameModes, sizeof(sGameModes));
	if( strcmp(sGameModes, "") )
	{
		Format(sGameModes, sizeof(sGameModes), ",%s,", sGameModes);
		if( StrContains(sGameModes, sGameMode, false) == -1 )
			return false;
	}

	GetConVarString(g_hCvarModesOff, sGameModes, sizeof(sGameModes));
	if( strcmp(sGameModes, "") )
	{
		Format(sGameModes, sizeof(sGameModes), ",%s,", sGameModes);
		if( StrContains(sGameModes, sGameMode, false) != -1 )
			return false;
	}

	return true;
}

public OnGamemode(const String:output[], caller, activator, Float:delay)
{
	if( strcmp(output, "OnCoop") == 0 )
		g_iCurrentMode = 1;
	else if( strcmp(output, "OnSurvival") == 0 )
		g_iCurrentMode = 2;
	else if( strcmp(output, "OnVersus") == 0 )
		g_iCurrentMode = 4;
	else if( strcmp(output, "OnScavenge") == 0 )
		g_iCurrentMode = 8;
}



// ====================================================================================================
//					EVENTS
// ====================================================================================================
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	ResetPlugin(false);
}

public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{		
	if( g_iPlayerSpawn == 1 && g_iRoundStart == 0 )
	{	 
		// 产生商店
		CreateTimer(15.0, tmrStart);
	}
	g_iRoundStart = 1;
}

public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{	
	if( g_iPlayerSpawn == 0 && g_iRoundStart == 1 )
	{
		// 产生商店
		CreateTimer(15.0, tmrStart);
	}
	g_iPlayerSpawn = 1;
}

public Action:tmrStart(Handle:timer)
{
	ResetPlugin();
	LoadSpawns();
}
// ====================================================================================================
//					LOAD SPAWNS
// ====================================================================================================
// 加载cfg，产生物品
LoadSpawns()
{
	if( g_bLoaded ) return;
	g_bLoaded = true;

	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
		return;

	// Load config
	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		CloseHandle(hFile);
		return;
	}

	// 获取当前地图数据
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);

	if( !KvJumpToKey(hFile, sMap) )
	{
		CloseHandle(hFile);
		return;
	}

	// 获取此地图物件个数
	new iCount = KvGetNum(hFile, "num", 0);
	if( iCount == 0 )
	{
		CloseHandle(hFile);
		return;
	}
	// 获取距离
	g_iCvarGlow = KvGetNum(hFile, "distance", 1000);

	// Spawn only a select few Gun Cabinets?
	new iIndexes[MAX_SPAWNS+1];
	if( iCount > MAX_SPAWNS )
		iCount = MAX_SPAWNS;


	// Spawn saved Gun Cabinets or create random
	new iRandom = iCount;
	if( iRandom != -1 )
	{
		for( new i = 1; i <= iCount; i++ )
			iIndexes[i-1] = i;

		SortIntegers(iIndexes, iCount, Sort_Random);	// 排序
	}

	// Get the weapon origins and spawn
	decl String:sTemp[10], Float:vPos[3], Float:vAng[3], String:sName[64];
	new index, buy_type;

	for( new i = 1; i <= iCount; i++ )
	{
		if( iRandom != -1 ) index = iIndexes[i-1];
		else index = i;

		IntToString(index, sTemp, sizeof(sTemp));

		if( KvJumpToKey(hFile, sTemp) )
		{
			KvGetVector(hFile, "ang", vAng);
			KvGetVector(hFile, "pos", vPos);
			KvGetString(hFile, "name", sName, 64);
			buy_type = KvGetNum(hFile, "type", 1);
			
			PrintToChatAll("Low物品名称%d", buy_type); 

			if( vPos[0] == 0.0 && vPos[1] == 0.0 && vPos[2] == 0.0 ) // Should never happen...
				LogError("Error: 0,0,0 origin. Iteration=%d. Index=%d. Random=%d. Count=%d.", i, index, iRandom, iCount);
			else
				CreateSpawn(vPos, vAng, index, sName, buy_type);
			KvGoBack(hFile);
		}
	}
	CloseHandle(hFile);
}

// ====================================================================================================
//					CREATE SPAWN
// ====================================================================================================
CreateSpawn(const Float:vOrigin[3], const Float:vAngles[3], index, String:sName[64], buy_type)
{
	if( g_iSpawnCount >= MAX_SPAWNS )
		return;

	new iSpawnIndex = -1;
	for( new i = 0; i < MAX_SPAWNS; i++ )
	{
		if( g_iSpawns[i][0] == 0 )
		{
			iSpawnIndex = i;
			break;
		}
	}

	if( iSpawnIndex == -1 )
		return;

   
	new entity = CreateEntityByName("prop_dynamic_override");
	if(!Entity_IsValid(entity)) return;
	g_iLastCreateEntity = entity;
	//PrintToChatAll("create ok %d", entity);
	Entity_SetName(entity, sName);
	//new entitytmp = Entity_FindByName(sName);
	//if(Entity_IsValid(entitytmp))
	//{
		//PrintToChatAll("find ok %d", entitytmp);
	//}
	//DispatchKeyValue(entity, "solid", "0");
	// 类型，是否可穿透等
	SetEntProp(entity, Prop_Send, "m_nSolidType", 6);
	// 模型
	if(buy_type == 1)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_1);
	}
	else if(buy_type == 2)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_2);
	}
	else if(buy_type == 3)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_3);
	}
	else if(buy_type == 4)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_4);
	}
	else if(buy_type == 5)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_5);
	}
	else if(buy_type == 6)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_6);
	}
	else if(buy_type == 7)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_7);
	}
	else if(buy_type == 8)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_8);
	}
	else if(buy_type == 9)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_9);
	}
	else if(buy_type == 10)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_10);
	}
	else if(buy_type == 11)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_11);
	}
	else if(buy_type == 12)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_12);
	}
	else if(buy_type == 13)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_13);
	}
	else if(buy_type == 14)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_14);
	}
	else if(buy_type == 15)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_15);
	}
	else if(buy_type == 16)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_16);
	}
	else if(buy_type == 17)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_17);
	}
	else if(buy_type == 18)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_18);
	}
	else if(buy_type == 19)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_19);
	}
	else if(buy_type == 20)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_20);
	}
	else if(buy_type == 21)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_21);
	}
	else if(buy_type == 22)
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_22);
	}
	else
	{
		SetEntityModel(entity, MODEL_BUY_TYPE_1);
	}
	// 坐标
	TeleportEntity(entity, vOrigin, vAngles, NULL_VECTOR);
	DispatchSpawn(entity);
	g_iSpawns[iSpawnIndex][0] = EntIndexToEntRef(entity);
	g_iSpawns[iSpawnIndex][1] = index;

	// 发光
	if( g_iCvarGlow )
	{
		SetEntProp(entity, Prop_Send, "m_nGlowRange", g_iCvarGlow);
		SetEntProp(entity, Prop_Send, "m_iGlowType", 1);
		SetEntProp(entity, Prop_Send, "m_glowColorOverride", g_iCvarGlowCol);
		AcceptEntityInput(entity, "StartGlowing");
	}	

	g_iSpawnCount++;
}

public SortFunc(x[], y[], array[][], Handle:data)
{
	if( x[1] > y[1])
		return -1;
	else if (x[1] < y[1])
		return 1;
	return 0;
}


MoveForward(const Float:vPos[3], const Float:vAng[3], Float:vReturn[3], Float:fDistance)
{
	decl Float:vDir[3];
	GetAngleVectors(vAng, vDir, NULL_VECTOR, NULL_VECTOR);
	vReturn = vPos;
	vReturn[0] += vDir[0] * fDistance;
	vReturn[1] += vDir[1] * fDistance;
	vReturn[2] += vDir[2] * fDistance;
}

MoveSideway(const Float:vPos[3], const Float:vAng[3], Float:vReturn[3], Float:fDistance)
{
	decl Float:vDir[3];
	GetAngleVectors(vAng, NULL_VECTOR, vDir, NULL_VECTOR);
	vReturn = vPos;
	vReturn[0] += vDir[0] * fDistance;
	vReturn[1] += vDir[1] * fDistance;
	vReturn[2] += vDir[2] * fDistance;
}
// ====================================================================================================
//					COMMANDS
// ====================================================================================================
//					sm_gun_cabinet
// ====================================================================================================
public Action:CmdCabinetMenu(client, args)
{
	if( !client )
	{
		ReplyToCommand(client, "[Gun Cabinet] Commands may only be used in-game on a dedicated server..");
		return Plugin_Handled;
	}
	else if( g_iSpawnCount >= MAX_SPAWNS )
	{
		PrintToChat(client, "%s错误：已达到上限。 (\x05%d/%d\x01).", CHAT_TAG, g_iSpawnCount, MAX_SPAWNS);
		return Plugin_Handled;
	}

	//DisplayMenu(g_hMenuList, client, MENU_TIME_FOREVER);
	//
	SelectTypePanel(client);

	return Plugin_Handled;
}
public SelectTypePanel(client)
{
	new Handle:menu = CreateMenu(SelectBuyTypeMenu);

	AddMenuItem(menu, "buy_type_1", "客巴");
	AddMenuItem(menu, "buy_type_2", "市内警车");
	AddMenuItem(menu, "buy_type_3", "市内计程车");
	AddMenuItem(menu, "buy_type_4", "叉车");
	AddMenuItem(menu, "buy_type_5", "火车厢");
	AddMenuItem(menu, "buy_type_6", "采访直升机(残骸)");
	AddMenuItem(menu, "buy_type_7", "沙发(高档真皮三座)");
	AddMenuItem(menu, "buy_type_8", "酒吧台");
	AddMenuItem(menu, "buy_type_9", "陈列架子(铁制）");
	AddMenuItem(menu, "buy_type_10", "草捆");
	AddMenuItem(menu, "buy_type_11", "街道垃圾桶II(塑料制绿色)");
	AddMenuItem(menu, "buy_type_12", "牛的尸体(一堆)");
	AddMenuItem(menu, "buy_type_13", "盒子堆(高版）");
	AddMenuItem(menu, "buy_type_14", "船货集装箱(白色打开的)");
	AddMenuItem(menu, "buy_type_15", "商场摆摊亭子A(关闭的）");
	AddMenuItem(menu, "buy_type_16", "自动贩卖机");
	AddMenuItem(menu, "buy_type_17", "高压电塔");
	AddMenuItem(menu, "buy_type_18", "大射灯(铁制落地）");
	AddMenuItem(menu, "buy_type_19", " 柏树群(带石头巨型)");
	AddMenuItem(menu, "buy_type_20", "床(家居式)");
	AddMenuItem(menu, "buy_type_21", "草丛III(翠绿色)");
	AddMenuItem(menu, "buy_type_22", "草丛IV(黄绿色带小花)");

	SetMenuTitle(menu, "请选择您的模型类型:");
	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

public SelectBuyTypeMenu(Handle:menu, MenuAction:action, client, param)
{
	if (!IsValidPlayer(client)) { return; }
	
	switch(action)
	{
	case MenuAction_End:
		{
			CloseHandle(menu);
		}
	case MenuAction_Select:
		{
			new String:item1[64];
			GetMenuItem(menu, param, item1, sizeof(item1));
			if (StrEqual(item1, "buy_type_1", false))
			{
				CmdCabinetSaveMenu(client, 1);
			}
			else if (StrEqual(item1, "buy_type_2", false))
			{
				CmdCabinetSaveMenu(client, 2);
			}
			else if (StrEqual(item1, "buy_type_3", false))
			{
				CmdCabinetSaveMenu(client, 3);
			}
			else if (StrEqual(item1, "buy_type_4", false))
			{
				CmdCabinetSaveMenu(client, 4);
			}
			else if (StrEqual(item1, "buy_type_5", false))
			{
				CmdCabinetSaveMenu(client, 5);
			}	
			else if (StrEqual(item1, "buy_type_6", false))
			{
				CmdCabinetSaveMenu(client, 6);
			}
			else if (StrEqual(item1, "buy_type_7", false))
			{
				CmdCabinetSaveMenu(client, 7);
			}
			else if (StrEqual(item1, "buy_type_8", false))
			{
				CmdCabinetSaveMenu(client, 8);
			}
			else if (StrEqual(item1, "buy_type_9", false))
			{
				CmdCabinetSaveMenu(client, 9);
			}
			else if (StrEqual(item1, "buy_type_10", false))
			{
				CmdCabinetSaveMenu(client, 10);
			}
			else if (StrEqual(item1, "buy_type_11", false))
			{
				CmdCabinetSaveMenu(client, 11);
			}
			else if (StrEqual(item1, "buy_type_12", false))
			{
				CmdCabinetSaveMenu(client, 12);
			}
			else if (StrEqual(item1, "buy_type_13", false))
			{
				CmdCabinetSaveMenu(client, 13);
			}
			else if (StrEqual(item1, "buy_type_14", false))
			{
				CmdCabinetSaveMenu(client, 14);
			}
			else if (StrEqual(item1, "buy_type_15", false))
			{
				CmdCabinetSaveMenu(client, 15);
			}	
			else if (StrEqual(item1, "buy_type_16", false))
			{
				CmdCabinetSaveMenu(client, 16);
			}
			else if (StrEqual(item1, "buy_type_17", false))
			{
				CmdCabinetSaveMenu(client, 17);
			}
			else if (StrEqual(item1, "buy_type_18", false))
			{
				CmdCabinetSaveMenu(client, 18);
			}
			else if (StrEqual(item1, "buy_type_19", false))
			{
				CmdCabinetSaveMenu(client, 19);
			}
			else if (StrEqual(item1, "buy_type_20", false))
			{
				CmdCabinetSaveMenu(client, 20);
			}
			else if (StrEqual(item1, "buy_type_21", false))
			{
				CmdCabinetSaveMenu(client, 21);
			}
			else if (StrEqual(item1, "buy_type_22", false))
			{
				CmdCabinetSaveMenu(client, 22);
			}
			else
			{
				CmdCabinetSaveMenu(client, 1);
			}	  
		}
	}
}
//
// ====================================================================================================
//					sm_gun_cabinet
// ===================================================================================================
CmdCabinetSaveMenu(client, buy_type)
{
	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
	{
		new Handle:hCfg = OpenFile(sPath, "w");
		WriteFileLine(hCfg, "");
		CloseHandle(hCfg);
	}

	// Load config
	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		PrintToChat(client, "%sError: Cannot read the Gun Cabinet config, assuming empty file. (\x05%s\x01).", CHAT_TAG, sPath);
	}

	// Check for current map in the config
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);
	if( !KvJumpToKey(hFile, sMap, true) )
	{
		PrintToChat(client, "%sError: Failed to add map to Gun Cabinet spawn config.", CHAT_TAG);
		CloseHandle(hFile);
		return;
	}

	// Retrieve how many Gun Cabinets are saved
	new iCount = KvGetNum(hFile, "num", 0);
	if( iCount >= MAX_SPAWNS )
	{
		PrintToChat(client, "%s错误：商店已达到上限。 (\x05%d/%d\x01).", CHAT_TAG, iCount, MAX_SPAWNS);
		CloseHandle(hFile);
		return;
	}

	// Save count
	iCount++;
	KvSetNum(hFile, "num", iCount);

	decl String:sTemp[10];

	IntToString(iCount, sTemp, sizeof(sTemp));
	// 距离
	new distance = KvGetNum(hFile, "distance", 1000);
	KvSetNum(hFile, "distance", distance);
	
	// 名字
	decl String:sName[64];
	GetCurrentMap(sName, 64);
	StrCat(sName, sizeof(sName), sTemp);

	if( KvJumpToKey(hFile, sTemp, true) )
	{
		new Float:vPos[3], Float:vAng[3];
		// Set player Gun Cabinet spawn location
		if( !SetTeleportEndPoint(client, vPos, vAng) )
		{
			PrintToChat(client, "%sCannot place Gun Cabinet, please try again.", CHAT_TAG);
			CloseHandle(hFile);
			return;
		}

		// Save angle / origin
		KvSetVector(hFile, "ang", vAng);
		KvSetVector(hFile, "pos", vPos);
		KvSetString(hFile, "name", sName);
		KvSetNum(hFile, "type", buy_type);
		CreateSpawn(vPos, vAng, iCount, sName, buy_type);

		// Save cfg
		KvRewind(hFile);
		KeyValuesToFile(hFile, sPath);

		PrintToChat(client, "%s(\x05%d/%d\x01) - Saved at pos:[\x05%f %f %f\x01] ang:[\x05%f %f %f\x01]", CHAT_TAG, iCount, MAX_SPAWNS, vPos[0], vPos[1], vPos[2], vAng[0], vAng[1], vAng[2]);
	}
	else
		PrintToChat(client, "%s(\x05%d/%d\x01) - Failed to save Gun Cabinet.", CHAT_TAG, iCount, MAX_SPAWNS);

	CloseHandle(hFile);
}

// ====================================================================================================
//					sm_gun_cabinet_del
// ====================================================================================================
public Action:CmdCabinetDel(client, args)
{
	if( !g_bCvarAllow )
	{
		ReplyToCommand(client, "[Gun Cabinet] Plugin turned off.");
		//return Plugin_Handled;
	}

	if( !client )
	{
		ReplyToCommand(client, "[Gun Cabinet] Commands may only be used in-game on a dedicated server..");
		return Plugin_Handled;
	}

	new entity = GetClientAimTarget(client, false);
	if( entity == -1 ) return Plugin_Handled;
	entity = EntIndexToEntRef(entity);

	new cfgindex, index = -1;
	for( new x = 0; x < MAX_SPAWNS; x++ )
	{
		if( g_iSpawns[x][0] == entity )
		{
			index = x;
			break;
		}
	}

	if( index == -1 )
	{
		return Plugin_Handled;
	}

	cfgindex = g_iSpawns[index][1];

	if( cfgindex == 0 )
	{
		RemoveSpawn(index);
		return Plugin_Handled;
	}

	for( new i = 0; i < MAX_SPAWNS; i++ )
	{
		if( g_iSpawns[i][1] > cfgindex )
			g_iSpawns[i][1]--;
	}

	g_iSpawnCount--;

	// Load config
	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
	{
		PrintToChat(client, "%sError: Cannot find the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, CONFIG_SPAWNS);
		return Plugin_Handled;
	}

	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		PrintToChat(client, "%sError: Cannot load the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, sPath);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	// Check for current map in the config
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);

	if( !KvJumpToKey(hFile, sMap) )
	{
		PrintToChat(client, "%s错误：当前地图没有发现商店。", CHAT_TAG);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	// Retrieve how many Gun Cabinets
	new iCount = KvGetNum(hFile, "num", 0);
	if( iCount == 0 )
	{
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	new bool:bMove;
	decl String:sTemp[16];

	// Move the other entries down
	for( new i = cfgindex; i <= iCount; i++ )
	{
		IntToString(i, sTemp, sizeof(sTemp));

		if( KvJumpToKey(hFile, sTemp) )
		{
			if( !bMove )
			{
				bMove = true;
				KvDeleteThis(hFile);
				RemoveSpawn(index);
			}
			else
			{
				IntToString(i-1, sTemp, sizeof(sTemp));
				KvSetSectionName(hFile, sTemp);
			}
		}

		KvRewind(hFile);
		KvJumpToKey(hFile, sMap);
	}

	if( bMove )
	{
		iCount--;
		KvSetNum(hFile, "num", iCount);

		// Save to file
		KvRewind(hFile);
		KeyValuesToFile(hFile, sPath);

		PrintToChat(client, "%s(\x05%d/%d\x01) - 单个商店已从配置中成功删除", CHAT_TAG, iCount, MAX_SPAWNS);
	}
	else
		PrintToChat(client, "%s(\x05%d/%d\x01) - Failed to remove Gun Cabinet from config.", CHAT_TAG, iCount, MAX_SPAWNS);

	CloseHandle(hFile);
	return Plugin_Handled;
}

public Action:CmdCabinetDelLast(client, args)
{
	if( !g_bCvarAllow )
	{
		ReplyToCommand(client, "[Gun Cabinet] Plugin turned off.");
		//return Plugin_Handled;
	}

	if( !IsValidPlayer(client) )
	{
		ReplyToCommand(client, "[Gun Cabinet] Commands may only be used in-game on a dedicated server..");
		return Plugin_Handled;
	}

	new entity = g_iLastCreateEntity;
	if(!Entity_IsValid(entity)) return Plugin_Handled;
	entity = EntIndexToEntRef(entity);

	new cfgindex, index = -1;
	for( new x = 0; x < MAX_SPAWNS; x++ )
	{
		if( g_iSpawns[x][0] == entity )
		{
			index = x;
			break;
		}
	}

	if( index == -1 )
	{
		return Plugin_Handled;
	}

	cfgindex = g_iSpawns[index][1];

	if( cfgindex == 0 )
	{
		RemoveSpawn(index);
		return Plugin_Handled;
	}

	for( new i = 0; i < MAX_SPAWNS; i++ )
	{
		if( g_iSpawns[i][1] > cfgindex )
			g_iSpawns[i][1]--;
	}

	g_iSpawnCount--;

	// Load config
	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
	{
		PrintToChat(client, "%sError: Cannot find the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, CONFIG_SPAWNS);
		return Plugin_Handled;
	}

	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		PrintToChat(client, "%sError: Cannot load the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, sPath);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	// Check for current map in the config
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);

	if( !KvJumpToKey(hFile, sMap) )
	{
		PrintToChat(client, "%s错误：当前地图没有发现商店。", CHAT_TAG);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	// Retrieve how many Gun Cabinets
	new iCount = KvGetNum(hFile, "num", 0);
	if( iCount == 0 )
	{
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	new bool:bMove;
	decl String:sTemp[16];

	// Move the other entries down
	for( new i = cfgindex; i <= iCount; i++ )
	{
		IntToString(i, sTemp, sizeof(sTemp));

		if( KvJumpToKey(hFile, sTemp) )
		{
			if( !bMove )
			{
				bMove = true;
				KvDeleteThis(hFile);
				RemoveSpawn(index);
			}
			else
			{
				IntToString(i-1, sTemp, sizeof(sTemp));
				KvSetSectionName(hFile, sTemp);
			}
		}

		KvRewind(hFile);
		KvJumpToKey(hFile, sMap);
	}

	if( bMove )
	{
		iCount--;
		KvSetNum(hFile, "num", iCount);

		// Save to file
		KvRewind(hFile);
		KeyValuesToFile(hFile, sPath);

		PrintToChat(client, "%s(\x05%d/%d\x01) - 单个商店已从配置中成功删除", CHAT_TAG, iCount, MAX_SPAWNS);
	}
	else
		PrintToChat(client, "%s(\x05%d/%d\x01) - Failed to remove Gun Cabinet from config.", CHAT_TAG, iCount, MAX_SPAWNS);

	g_iLastCreateEntity = -1;
	
	CloseHandle(hFile);
	return Plugin_Handled;
}


// ====================================================================================================
//					sm_gun_cabinet_clear
// ====================================================================================================
public Action:CmdCabinetClear(client, args)
{
	if( !client )
	{
		ReplyToCommand(client, "[Gun Cabinet] Commands may only be used in-game on a dedicated server..");
		return Plugin_Handled;
	}

	ResetPlugin();

	PrintToChat(client, "%s(0/%d) - 从地图中删除所有商店。", CHAT_TAG, MAX_SPAWNS);
	return Plugin_Handled;
}

// ====================================================================================================
//					sm_gun_cabinet_wipe
// ====================================================================================================
public Action:CmdCabinetWipe(client, args)
{
	if( !client )
	{
		ReplyToCommand(client, "[Gun Cabinet] Commands may only be used in-game on a dedicated server..");
		return Plugin_Handled;
	}

	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
	{
		PrintToChat(client, "%sError: Cannot find the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, sPath);
		return Plugin_Handled;
	}

	// Load config
	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		PrintToChat(client, "%sError: Cannot load the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, sPath);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	// Check for current map in the config
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);

	if( !KvJumpToKey(hFile, sMap, false) )
	{
		PrintToChat(client, "%s错误：当前地图没有发现商店。", CHAT_TAG);
		CloseHandle(hFile);
		return Plugin_Handled;
	}

	KvDeleteThis(hFile);
	ResetPlugin();

	// Save to file
	KvRewind(hFile);
	KeyValuesToFile(hFile, sPath);
	CloseHandle(hFile);

	PrintToChat(client, "%s(0/%d) - 本小张所有商店已从配置文件 \x05sm_gun_cabinet_save \x01中成功删除.", CHAT_TAG, MAX_SPAWNS);
	return Plugin_Handled;
}

// ====================================================================================================
//					sm_gun_cabinet_glow
// ====================================================================================================
public Action:CmdCabinetGlow(client, args)
{
	static bool:glow;
	glow = !glow;
	PrintToChat(client, "%sGlow has been turned %s", CHAT_TAG, glow ? "on" : "off");

	VendorGlow(glow);
	return Plugin_Handled;
}

VendorGlow(glow)
{
	new ent;

	for( new i = 0; i < MAX_SPAWNS; i++ )
	{
		ent = g_iSpawns[i][0];
		if( IsValidEntRef(ent) )
		{
			SetEntProp(ent, Prop_Send, "m_iGlowType", glow ? 3 : 0);
			if( glow )
			{
				SetEntProp(ent, Prop_Send, "m_glowColorOverride", 65535);
				SetEntProp(ent, Prop_Send, "m_nGlowRange", 0);
			}
		}
	}
}

// ====================================================================================================
//					sm_gun_cabinet_list
// ====================================================================================================
public Action:CmdCabinetList(client, args)
{
	decl Float:vPos[3];
	new count;
	for( new i = 0; i < MAX_SPAWNS; i++ )
	{
		if( IsValidEntRef(g_iSpawns[i][0]) )
		{
			count++;
			GetEntPropVector(g_iSpawns[i][0], Prop_Data, "m_vecOrigin", vPos);
			PrintToChat(client, "%s%d) %f %f %f", CHAT_TAG, i+1, vPos[0], vPos[1], vPos[2]);
		}
	}
	PrintToChat(client, "%sTotal: %d.", CHAT_TAG, count);
	return Plugin_Handled;
}

// ====================================================================================================
//					sm_gun_cabinet_reload
// ====================================================================================================
public Action:CmdCabinetReload(client, args)
{
	CloseHandle(g_hMenuList);

	g_bCvarAllow = false;
	ResetPlugin(true);
	IsAllowed();
	return Plugin_Handled;
}

// ====================================================================================================
//					sm_gun_cabinet_tele
// ====================================================================================================
public Action:CmdCabinetTele(client, args)
{
	if( args == 1 )
	{
		decl String:arg[16];
		GetCmdArg(1, arg, 16);
		new index = StringToInt(arg) - 1;
		if( index > -1 && index < MAX_SPAWNS && IsValidEntRef(g_iSpawns[index][0]) )
		{
			decl Float:vPos[3], Float:vAng[3];
			GetEntPropVector(g_iSpawns[index][0], Prop_Data, "m_vecOrigin", vPos);
			GetEntPropVector(g_iSpawns[index][0], Prop_Send, "m_angRotation", vAng);
			MoveForward(vPos, vAng, vPos, 30.0);
			vPos[2] += 20.0;
			TeleportEntity(client, vPos, NULL_VECTOR, NULL_VECTOR);
			PrintToChat(client, "%sTeleported to %d.", CHAT_TAG, index + 1);
			return Plugin_Handled;
		}

		PrintToChat(client, "%sCould not find index for teleportation.", CHAT_TAG);
	}
	else
		PrintToChat(client, "%sUsage: sm_gun_cabinet_tele <index 1-%d>.", CHAT_TAG, MAX_SPAWNS);
	return Plugin_Handled;
}

// ====================================================================================================
//					MENU ANGLE
// ====================================================================================================
public Action:CmdCabinetAng(client, args)
{
	ShowMenuAng(client);
	return Plugin_Handled;
}

ShowMenuAng(client)
{
	CreateMenus();
	DisplayMenu(g_hMenuAng, client, MENU_TIME_FOREVER);
}

public AngMenuHandler(Handle:menu, MenuAction:action, client, index)
{
	if( action == MenuAction_Select )
	{
		if( index == 6 )
			SaveData(client);
		else
			SetAngle(client, index);
		ShowMenuAng(client);
	}
}

SetAngle(client, index)
{
	new aim = GetClientAimTarget(client, false);
	if( aim != -1 )
	{
		new Float:vAng[3], entity;
		aim = EntIndexToEntRef(aim);

		for( new i = 0; i < MAX_SPAWNS; i++ )
		{
				if( g_iSpawns[i][0] == aim  )
				{
					entity = g_iSpawns[i][0];

					GetEntPropVector(entity, Prop_Send, "m_angRotation", vAng);

					if( index == 0 ) vAng[0] += 5.0;
					else if( index == 1 ) vAng[1] += 5.0;
					else if( index == 2 ) vAng[2] += 5.0;
					else if( index == 3 ) vAng[0] -= 5.0;
					else if( index == 4 ) vAng[1] -= 5.0;
					else if( index == 5 ) vAng[2] -= 5.0;

					TeleportEntity(entity, NULL_VECTOR, vAng, NULL_VECTOR);

					PrintToChat(client, "%s新角度调整: %f %f %f", CHAT_TAG, vAng[0], vAng[1], vAng[2]);
					break;
				}
		}
		
	}
}

// ====================================================================================================
//					MENU ORIGIN 
// ====================================================================================================
public Action:CmdCabinetPos(client, args)
{
	ShowMenuPos(client);
	return Plugin_Handled;
}

ShowMenuPos(client)
{
	CreateMenus();
	DisplayMenu(g_hMenuPos, client, MENU_TIME_FOREVER);
}

public PosMenuHandler(Handle:menu, MenuAction:action, client, index)
{
	if( action == MenuAction_Select )
	{
		if( index == 6 )
			SaveData(client);
		else
			SetOrigin(client, index);
		ShowMenuPos(client);
	}
}

SetOrigin(client, index)
{
	new aim = GetClientAimTarget(client, false);
	if( aim != -1 )
	{
		// 设置坐标
		new Float:vPos[3], entity;
		aim = EntIndexToEntRef(aim);

		for( new i = 0; i < MAX_SPAWNS; i++ )
		{
				if( g_iSpawns[i][0] == aim  )
				{
					entity = g_iSpawns[i][0];

					GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vPos);

					if( index == 0 ) vPos[0] += 5.0;
					else if( index == 1 ) vPos[1] += 5.0;
					else if( index == 2 ) vPos[2] += 5.0;
					else if( index == 3 ) vPos[0] -= 5.0;
					else if( index == 4 ) vPos[1] -= 5.0;
					else if( index == 5 ) vPos[2] -= 5.0;

					TeleportEntity(entity, vPos, NULL_VECTOR, NULL_VECTOR);

					PrintToChat(client, "%s新位置调整: %f %f %f", CHAT_TAG, vPos[0], vPos[1], vPos[2]);
					break;
				}
		}
	}
}

SaveData(client)
{
	new entity = GetClientAimTarget(client, false);
	if( entity == -1 )
		return;

	entity = EntIndexToEntRef(entity);

	new cfgindex, index = -1;
	for( new x = 0; x < MAX_SPAWNS; x++ )
	{
			if( g_iSpawns[x][0] == entity )
			{
				entity = g_iSpawns[x][0];
				cfgindex = g_iSpawns[x][1];
				index = x;
				break;
			}
	}

	if( index == -1 )
	{
		PrintToChat(client, "%sError: Cannot find the target.", CHAT_TAG, CONFIG_SPAWNS);
		return;
	}

	// Load config
	decl String:sPath[PLATFORM_MAX_PATH];
	BuildPath(Path_SM, sPath, sizeof(sPath), "%s", CONFIG_SPAWNS);
	if( !FileExists(sPath) )
	{
		PrintToChat(client, "%sError: Cannot find the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, CONFIG_SPAWNS);
		return;
	}

	new Handle:hFile = CreateKeyValues("spawns");
	if( !FileToKeyValues(hFile, sPath) )
	{
		PrintToChat(client, "%sError: Cannot load the Gun Cabinet config (\x05%s\x01).", CHAT_TAG, sPath);
		CloseHandle(hFile);
		return;
	}

	// Check for current map in the config
	decl String:sMap[64];
	GetCurrentMap(sMap, 64);

	if( !KvJumpToKey(hFile, sMap) )
	{
		PrintToChat(client, "%s错误：当前地图没有发现商店。", CHAT_TAG);
		CloseHandle(hFile);
		return;
	}

	decl Float:vAng[3], Float:vPos[3], String:sTemp[32];
	GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vPos);
	GetEntPropVector(entity, Prop_Send, "m_angRotation", vAng);

	IntToString(cfgindex, sTemp, sizeof(sTemp));
	if( KvJumpToKey(hFile, sTemp) )
	{
		KvSetVector(hFile, "ang", vAng);
		KvSetVector(hFile, "pos", vPos);

		// Save cfg
		KvRewind(hFile);
		KeyValuesToFile(hFile, sPath);

		PrintToChat(client, "%s修改以保存", CHAT_TAG);
	}
}

CreateMenus()
{
	if( g_hMenuAng == INVALID_HANDLE )
	{
		g_hMenuAng = CreateMenu(AngMenuHandler);
		AddMenuItem(g_hMenuAng, "", "X + 1.0");
		AddMenuItem(g_hMenuAng, "", "Y + 1.0");
		AddMenuItem(g_hMenuAng, "", "Z + 1.0");
		AddMenuItem(g_hMenuAng, "", "X - 1.0");
		AddMenuItem(g_hMenuAng, "", "Y - 1.0");
		AddMenuItem(g_hMenuAng, "", "Z - 1.0");
		AddMenuItem(g_hMenuAng, "", "保存修改");
		SetMenuTitle(g_hMenuAng, "Set Angle");
		SetMenuExitButton(g_hMenuAng, true);
	}

	if( g_hMenuPos == INVALID_HANDLE )
	{
		g_hMenuPos = CreateMenu(PosMenuHandler);
		AddMenuItem(g_hMenuPos, "", "X + 0.5");
		AddMenuItem(g_hMenuPos, "", "Y + 0.5");
		AddMenuItem(g_hMenuPos, "", "Z + 0.5");
		AddMenuItem(g_hMenuPos, "", "X - 0.5");
		AddMenuItem(g_hMenuPos, "", "Y - 0.5");
		AddMenuItem(g_hMenuPos, "", "Z - 0.5");
		AddMenuItem(g_hMenuPos, "", "保存修改");
		SetMenuTitle(g_hMenuPos, "Set Position");
		SetMenuExitButton(g_hMenuPos, true);
	}
}



// ====================================================================================================
//					STUFF
// ====================================================================================================
bool:IsValidEntRef(entity)
{
	if( entity && EntRefToEntIndex(entity) != INVALID_ENT_REFERENCE )
		return true;
	return false;
}

ResetPlugin(bool:all = true)
{
	g_bLoaded = false;
	g_iSpawnCount = 0;
	g_iRoundStart = 0;
	g_iPlayerSpawn = 0;

	if( all )
		for( new i = 0; i < MAX_SPAWNS; i++ )
			RemoveSpawn(i);
}

RemoveSpawn(index)
{
	new entity, client;

	for( new x = 0; x < MAX_SLOTS + 2; x++ )
	{
		entity = g_iSpawns[index][x];
		g_iSpawns[index][x] = 0;

		if( x != 1 && IsValidEntRef(entity) )
		{
			if( x > 1 )
			{
				client = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity");
				if( client < 0 || client > MaxClients || !IsClientInGame(client) )
				{
					AcceptEntityInput(entity, "kill");
				}
			} else {
				AcceptEntityInput(entity, "kill");
			}
		}
	}
}

// ====================================================================================================
//					POSITION
// ====================================================================================================
Float:GetGroundHeight(Float:vPos[3])
{
	new Float:vAng[3], Handle:trace = TR_TraceRayFilterEx(vPos, Float:{ 90.0, 0.0, 0.0 }, MASK_ALL, RayType_Infinite, _TraceFilter);
	if( TR_DidHit(trace) )
		TR_GetEndPosition(vAng, trace);

	CloseHandle(trace);
	return vAng[2];
}

// Taken from "[L4D2] Weapon/Zombie Spawner"
// By "Zuko & McFlurry"
SetTeleportEndPoint(client, Float:vPos[3], Float:vAng[3])
{
	GetClientEyePosition(client, vPos);
	GetClientEyeAngles(client, vAng);

	new Handle:trace = TR_TraceRayFilterEx(vPos, vAng, MASK_SHOT, RayType_Infinite, _TraceFilter);

	if(TR_DidHit(trace))
	{
		decl Float:vNorm[3];
		new Float:degrees = vAng[1];
		TR_GetEndPosition(vPos, trace);
		GetGroundHeight(vPos);
		TR_GetPlaneNormal(trace, vNorm);
		GetVectorAngles(vNorm, vAng);
		if( vNorm[2] == 1.0 )
		{
			vAng[0] = 0.0;
			vAng[1] = degrees + 180;
		}
		else
		{
			if( degrees > vAng[1] )
				degrees = vAng[1] - degrees;
			else
				degrees = degrees - vAng[1];
			vAng[0] += 90.0;
			RotateYaw(vAng, degrees + 180);
		}
	}
	else
	{
		CloseHandle(trace);
		return false;
	}
	CloseHandle(trace);
	return true;
}

public bool:_TraceFilter(entity, contentsMask)
{
	return entity > MaxClients || !entity;
}



//---------------------------------------------------------
// do a specific rotation on the given angles
//---------------------------------------------------------
RotateYaw( Float:angles[3], Float:degree )
{
	decl Float:direction[3], Float:normal[3];
	GetAngleVectors( angles, direction, NULL_VECTOR, normal );

	new Float:sin = Sine( degree * 0.01745328 );	 // Pi/180
	new Float:cos = Cosine( degree * 0.01745328 );
	new Float:a = normal[0] * sin;
	new Float:b = normal[1] * sin;
	new Float:c = normal[2] * sin;
	new Float:x = direction[2] * b + direction[0] * cos - direction[1] * c;
	new Float:y = direction[0] * c + direction[1] * cos - direction[2] * a;
	new Float:z = direction[1] * a + direction[2] * cos - direction[0] * b;
	direction[0] = x;
	direction[1] = y;
	direction[2] = z;

	GetVectorAngles( direction, angles );

	decl Float:up[3];
	GetVectorVectors( direction, NULL_VECTOR, up );

	new Float:roll = GetAngleBetweenVectors( up, normal, direction );
	angles[2] += roll;
}

//---------------------------------------------------------
// calculate the angle between 2 vectors
// the direction will be used to determine the sign of angle (right hand rule)
// all of the 3 vectors have to be normalized
//---------------------------------------------------------
Float:GetAngleBetweenVectors( const Float:vector1[3], const Float:vector2[3], const Float:direction[3] )
{
	decl Float:vector1_n[3], Float:vector2_n[3], Float:direction_n[3], Float:cross[3];
	NormalizeVector( direction, direction_n );
	NormalizeVector( vector1, vector1_n );
	NormalizeVector( vector2, vector2_n );
	new Float:degree = ArcCosine( GetVectorDotProduct( vector1_n, vector2_n ) ) * 57.29577951;   // 180/Pi
	GetVectorCrossProduct( vector1_n, vector2_n, cross );

	if ( GetVectorDotProduct( cross, direction_n ) < 0.0 )
		degree *= -1.0;

	return degree;
}

/* 判断玩家是否有效 */
stock bool:IsValidPlayer(Client, bool:AllowBot = true, bool:AllowDeath = true)
{
	if (Client < 1 || Client > MaxClients)
		return false;
	if (!IsClientConnected(Client) || !IsClientInGame(Client))
		return false;
	if (!AllowBot)
	{
		if (IsFakeClient(Client))
			return false;
	}
	if (!AllowDeath)
	{
		if (!IsPlayerAlive(Client))
			return false;
	}		
	return true;
}